using Fusion.Frameworks.Assets;
using Fusion.Utilities;
using Fusion.Utilities.Singleton;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Fusion.Frameworks.UI
{

    /// <summary>
    /// 用于自定义生成UIObject
    /// 例如：Hotfix中 使用DLLManager.Instance.Instantiate<UIObject>方法
    /// </summary>
    public interface IUIGenerator
    {
        UIObject Generate(string name, UIData data);
    }

    /// <summary>
    /// UI页面管理
    /// </summary>
    [DisallowMultipleComponent]
    public class UIManager : SingletonBehaviour<UIManager>
    {
        public GameObject CanvasObject { get => canvasObject; }
        public Camera Camera { get => camera; }
        public GameObject Root { get => root; }

        private GameObject canvasObject = null;
        private GameObject rootObject = null;
        private new Camera camera = null;
        private GameObject root = null;

        private List<UIObject> objectList = new();
        private Dictionary<string, AsyncHandlingData> asyncHandling = new Dictionary<string, AsyncHandlingData>();

        private class AsyncHandlingData {
            public UIObject uiObject = null;
            public int objectIndex = 0;
        }

        public int Count
        {
            get
            {
                return objectList.Count;
            }
        }

        public bool CheckUIExists(string name)
        {
            return GetTopUIIndex(name) >= 0;
        }

        public bool CheckUIExists(string name, out UIObject ui)
        {
            List<UIObject> objectList = GetCurrentUIObjectList();

            for (int i = objectList.Count - 1; i >= 0; i--)
            {
                UIObject uiObject = objectList[i];
                if (uiObject.Data.Name == name)
                {
                    ui = uiObject;
                    return true;
                }
            }
            ui = null;
            return false;
        }

        private void ConfigCanvas(UIObject uiObject)
        {
            Canvas canvas = uiObject.GameObject.GetOrAddComponent<Canvas>();
            uiObject.GameObject.GetOrAddComponent<GraphicRaycaster>();
            canvas.SetOverrideSorting(true);
            canvas.vertexColorAlwaysGammaSpace = true;
            canvas.sortingOrder = uiObject.SortingOrder;
        }


        /// <summary>
        /// 同步运行UI页面，页面会被UIManager管理
        /// </summary>
        /// <param name="path">UI Prefab的路径</param>
        /// <param name="data">UI页面数据</param>
        /// <returns></returns>
        public UIObject Launch(string path, UIData data = null)
        {
            if (data == null)
            {
                data = new UIData();
            }
            if (data.Name == null)
            {
                data.Name = path;
            }
            if (data.LaunchMode == UILaunchMode.Standard)
            {
                UIObject uiObject = CreateUIObject(path, data);
                int objectIndex = HandleUILaunchData(uiObject);
                RefreshSortingOrder(objectIndex);
                return uiObject;
            } else
            {
                int index = GetTopUIIndex(path);
                if (index >= 0)
                {
                    return LaunchSingleTop(index, data);
                } else
                {
                    UIObject uiObject = CreateUIObject(path, data);
                    int objectIndex = HandleUILaunchData(uiObject);
                    RefreshSortingOrder(objectIndex);
                    return uiObject;
                }
            }
        }

        /// <summary>
        /// 同步创建UI自由页面，不会被UIManager管理
        /// </summary>
        /// <param name="path">UI Prefab的路径</param>
        /// <param name="data">UI页面数据</param>
        /// <param name="rootObject">页面父节点</param>
        /// <returns></returns>
        public UIObject CreateUIObject(string path, UIData data = null, GameObject rootObject = null, bool independentCanvas = true)
        {
            if (rootObject == null)
            {
                rootObject = this.rootObject;
            }
            if (data == null)
            {
                data = new UIData();
            }
            if (data.Name == null)
            {
                data.Name = path;
            }
            GameObject gameObject = data.GameObject ? data.GameObject : AssetsUtility.CreateGameObject(data.Name, rootObject);
            string className = path.Replace("/", ".");
            UIObject uiObject = InstantiateUIObject(className, data);
            uiObject.GameObject = gameObject;
            if (independentCanvas)
            {
                ConfigCanvas(uiObject);
            }
            //bool oldActive = uiObject.Active;
            //uiObject.SetActive(true);
            UIExecutor uiExecutor = uiObject.GameObject.GetOrAddComponent<UIExecutor>();
            uiExecutor.StartEvent += uiObject.RestoreActive;
            uiExecutor.StartEvent += uiObject.Init;
            uiExecutor.StartEvent += uiObject.Update;
            uiExecutor.StartEvent += uiObject.FinishFirstUpdate;
            //uiObject.SetActive(oldActive);

            return uiObject;
        }

        /// <summary>
        /// 异步运行UI页面，页面会被UIManager管理，同一个Prefab只能同时运行一个
        /// </summary>
        /// <param name="path">UI Prefab的路径</param>
        /// <param name="data">UI页面数据</param>
        /// <returns></returns>
        public UIObject LaunchAsync(string path, UIData data = null)
        {
#if FUSION_ASSETBUNDLE || !UNITY_EDITOR
            if (data == null)
            {
                data = new UIData();
            }
            if (data.Name == null)
            {
                data.Name = path;
            }
            if (data.GameObject != null)
            {
                return Launch(path, data);
            }
            else
            {
                if (data.LaunchMode == UILaunchMode.Standard)
                {
                    return CreateUIObjectAsyncHandling(path, data);
                }
                else
                {
                    int index = GetTopUIIndex(path);
                    if (index >= 0)
                    {
                        return LaunchSingleTop(index, data);
                    }
                    else
                    {
                        return CreateUIObjectAsyncHandling(path, data);
                    }
                }
            }
#else
            return Launch(path, data);
#endif
        }

        private UIObject InstantiateUIObject(string name, UIData data)
        {
            UIObject uiObject;
            if (uiGenerator != null)
            {
                uiObject = uiGenerator.Generate(name, data);
            }
            else
            {
                Type classType = Type.GetType($"{name}, Assembly-CSharp");
                uiObject = classType != null ? (UIObject)Activator.CreateInstance(classType, data) : default(UIObject);
            }
            return uiObject;
        }

        /// <summary>
        /// 异步创建UI自由页面，不会被UIManager管理
        /// </summary>
        /// <param name="path">UI Prefab的路径</param>
        /// <param name="data">UI页面数据</param>
        /// <param name="rootObject">页面父节点</param>
        /// <param name="finishCallback">创建完成后回调</param>
        /// <returns></returns>
        public UIObject CreateUIObjectAsync(string path, UIData data = null, GameObject rootObject = null, Action<UIObject> finishCallback = null, bool independentCanvas = true)
        {
            if (rootObject == null)
            {
                rootObject = this.rootObject;
            }
            if (data == null)
            {
                data = new UIData();
            }
            if (data.Name == null)
            {
                data.Name = path;
            }
            string className = path.Replace("/", ".");
            UIObject uiObject = InstantiateUIObject(className, data);
            AssetsUtility.CreateGameObjectAsync(data.Name, rootObject, false, delegate(GameObject gameObject)
            {
                uiObject.GameObject = gameObject;
                if (independentCanvas)
                {
                    ConfigCanvas(uiObject);
                }
                //bool oldActive = uiObject.Active;
                //uiObject.SetActive(true);
                UIExecutor uiExecutor = uiObject.GameObject.GetOrAddComponent<UIExecutor>();
                uiExecutor.StartEvent += uiObject.RestoreActive;
                uiExecutor.StartEvent += uiObject.Init;
                uiExecutor.StartEvent += uiObject.Update;
                uiExecutor.StartEvent += uiObject.FinishFirstUpdate;
                //uiObject.SetActive(oldActive);
                if (finishCallback != null)
                {
                    finishCallback(uiObject);
                }

            });

            return uiObject;
        }

        /// <summary>
        /// 关闭UI页面，正在异步加载中的页面不能被关闭
        /// </summary>
        /// <param name="uiObject">UI对象</param>
        /// <param name="updateLast">是否更新上一个页面</param>
        public void Finish(UIObject uiObject, bool updateLast = false)
        {
            if (!uiObject.CheckValid())
            {
                return;
            }

            int index = GetTopUIIndex(uiObject);
            if (index >= 0)
            {
                List<UIObject> objectList = GetCurrentUIObjectList();
                objectList.RemoveAt(index);
                RefreshSortingOrder(index);
                if (uiObject.Active && index > 0)
                {
                    int lastWindowIndex = Math.Clamp(GetLastWindowIndex(), 0, objectList.Count - 1);
                    for (int i = lastWindowIndex; i < objectList.Count; i++)
                    {
                        UIObject windowUIObject = objectList[i];
                        windowUIObject.Show();
                    }
                    UIObject lastUIObject = objectList[index - 1];

                    if (updateLast)
                    {
                        lastUIObject.Update();
                    }
                }
            }
            uiObject.OnFinished();
            uiObject.OnDestroy();
            AssetsUtility.Release(uiObject.GameObject);
            uiObject.GameObject = null;
        }

        public void Clear(bool clearAssets = true)
        {
            for (int index = 0; index < objectList.Count; index++)
            {
                UIObject uiObject = objectList[index];
                uiObject.OnDestroy();
                if (clearAssets)
                {
                    AssetsUtility.ReleaseImmediate(uiObject.GameObject);
                }
                else
                {
                    Destroy(uiObject.GameObject);
                }
                uiObject.GameObject = null;
            }
            objectList.Clear();
            asyncHandling.Clear();
        }

        public void Restore(List<UIObject> restoreList)
        {
            Clear();
            for (int index = 0; index < restoreList.Count; index++)
            {
                UIObject uiObject = restoreList[index];
                if (uiObject.GameObject == null)
                {
                    GameObject gameObject = AssetsUtility.CreateGameObject(uiObject.Data.Name, rootObject);
                    uiObject.GameObject = gameObject;
                    ConfigCanvas(uiObject);
                }
                objectList.Add(uiObject);
                //bool oldActive = uiObject.Active;
                UIExecutor uiExecutor = uiObject.GameObject.GetOrAddComponent<UIExecutor>();
                uiExecutor.StartEvent += uiObject.RestoreActive;
                uiExecutor.StartEvent += uiObject.Init;
                uiExecutor.StartEvent += uiObject.Update;
                if (!uiObject.Active)
                {
                    uiExecutor.StartEvent += uiObject.Hide;
                }
                uiExecutor.StartEvent += uiObject.FinishFirstUpdate;
                //uiObject.SetActive(oldActive);
            }

            //int lastWindowIndex = Math.Clamp(GetLastWindowIndex(), 0, objectList.Count - 1);
            //for (int i = lastWindowIndex; i < objectList.Count; i++)
            //{
            //    UIObject windowUIObject = objectList[i];
            //    windowUIObject.SetActive(true);
            //}
            RefreshSortingOrder(0);
        }

        public void ForEach(Action<UIObject> foreachCallback)
        {
            for (int index = 0; index < objectList.Count; index++)
            {
                foreachCallback(objectList[index]);
            }
        }

        private void RefreshSortingOrder(int index)
        {
            for (int i = index; i < objectList.Count; i++)
            {
                UIObject uiObject = objectList[i];
                Canvas canvas = uiObject.GameObject.GetComponent<Canvas>();
                if (canvas != null)
                {
                    canvas.sortingOrder = i * uiObject.SortingOrder;
                }
            }
        }
        private UIObject CreateUIObjectAsyncHandling(string path, UIData data, bool independentCanvas = true)
        {
            if (asyncHandling.ContainsKey(path))
            {
                return asyncHandling[path].uiObject;
            }
            else
            {
                UIObject uiObject = CreateUIObjectAsync(path, data, null, delegate (UIObject uiObject)
                {
                    if (asyncHandling.ContainsKey(path) && objectList.Contains(uiObject))
                    {
                        RefreshSortingOrder(asyncHandling[path].objectIndex);
                        asyncHandling.Remove(path);
                    } else
                    {
                        AssetsUtility.Release(uiObject.GameObject);
                    }
                }, independentCanvas);
                asyncHandling[path] = new AsyncHandlingData();
                asyncHandling[path].uiObject = uiObject;
                asyncHandling[path].objectIndex = HandleUILaunchData(uiObject);
                return uiObject;
            }
        }

        private List<UIObject> GetCurrentUIObjectList()
        {
            return objectList;
        }
     
        private int GetLastWindowIndex()
        {
            List<UIObject> objectList = GetCurrentUIObjectList();
            int index = -1;
            for (int i=objectList.Count - 1; i >= 0; i--)
            {
                if (objectList[i].Type == UIType.Window)
                {
                    index = i;
                    break;
                }
            }

            return index;
        }

        private int HandleUILaunchData(UIObject uiObject)
        {
            List<UIObject> objectList = GetCurrentUIObjectList();
            int lastWindowIndex = GetLastWindowIndex();
            int insertIndex = 0;
            int objectListMaxIndex = objectList.Count - 1;
            for (int index = objectListMaxIndex; index >= 0; index--)
            {
                UIObject obj = objectList[index];
                if (obj.SortingOrder <= uiObject.SortingOrder)
                {
                    insertIndex = index + 1;
                    break;
                }
            }
            objectList.Insert(insertIndex, uiObject);
            if (uiObject.Type == UIType.Window && lastWindowIndex >= 0)
            {
                for (int i = lastWindowIndex; i < insertIndex; i++)
                {
                    objectList[i].Hide();
                }
            }
            if (insertIndex > lastWindowIndex)
            {
                uiObject.Show();
            }
            else
            {
                uiObject.Hide();
            }

            return insertIndex;
        }

        private int GetTopUIIndex(UIObject uiObject)
        {
            return GetTopUIIndex(uiObject.Data.Name);
        }
        private int GetTopUIIndex(string name)
        {
            List<UIObject> objectList = GetCurrentUIObjectList();

            int index = -1;

            for (int i=objectList.Count - 1; i >= 0; i--)
            {
                UIObject uiObject = objectList[i];
                if (uiObject.Data.Name == name)
                {
                    index = i;
                    break;
                }
            }

            return index;
        }

        private UIObject LaunchSingleTop(int index, UIData data)
        {
            List<UIObject> objectList = GetCurrentUIObjectList();
            for (int i = index + 1; i < objectList.Count; i++) 
            {
                if (objectList[i].LifeType != UILifeType.Persistent)
                {
                    AssetsUtility.Release(objectList[i].GameObject);
                }
            }
            int removeIndex = index + 1;
            objectList.RemoveRange(removeIndex, objectList.Count - removeIndex);

            UIObject uiObject = objectList[index];
            uiObject.Data = data;
            uiObject.Show();
            uiObject.Update();
            return uiObject;
        }

        private IUIGenerator uiGenerator = null;

        public override void Initializate()
        {
            base.Initializate();
            instance.root = GameObject.Find("UI");
            if (instance.root == null)
            {
                Debug.LogError("Can not find UI in Hierarchy, please drag it from Prefab/UI/UI");
            }
            else
            {
                GameObject canvasObject = Utility.Find(instance.root, "Canvas");
                instance.camera = Utility.Find<Camera>(instance.root, "UICamera");
                instance.canvasObject = canvasObject;
                Transform rootTransform = canvasObject.transform.Find("Root");
                if (rootTransform == null)
                {
                    GameObject rootObject = new GameObject("Root");
                    RectTransform rectTransform = rootObject.AddComponent<RectTransform>();
                    rootObject.transform.SetParent(canvasObject.transform);
                    rectTransform.anchorMin = Vector2.zero;
                    rectTransform.anchorMax = Vector2.one;
                    rectTransform.anchoredPosition3D = Vector3.zero;
                    rectTransform.localScale = Vector3.one;
                    rectTransform.sizeDelta = Vector2.zero;
                    instance.rootObject = rootObject;
                }
                else
                {
                    instance.rootObject = rootTransform.gameObject;
                }

                Type uiGeneratorType = Type.GetType($"Fusion.Hotfix.Generator.UIGenerator, Assembly-CSharp");
                if (uiGeneratorType != null)
                {
                    uiGenerator = (IUIGenerator)Activator.CreateInstance(uiGeneratorType);
                }
            }
        }
    }
}


